package cc.cix.hq.file.util;

import cn.hutool.json.JSONUtil;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@RequiredArgsConstructor
public class MinioUtil {

    private static MinioClient minioClientInstance;

    private final MinioClient minioClient;

    @PostConstruct
    private void init() {
        minioClientInstance = minioClient;
    }

    /**
     * 创建存储桶
     *
     * @param bucketName 存储桶名称
     * @return
     */
    public static String makeBucket(String bucketName) throws Exception {
        // 判断存储桶存不存在
        boolean bucketExists = minioClientInstance.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (bucketExists) {
            log.info("Minio 桶已经存在了:{}", bucketName);
        } else {
            // 创建桶
            minioClientInstance.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            log.info("Minio 桶创建成功：{}", bucketName);
        }
        return bucketName;
    }

    /**
     * 查询全部存储桶信息
     *
     * @return
     * @throws Exception
     */
    public static List<Bucket> listBuckets() throws Exception {
        List<Bucket> listBuckets = minioClientInstance.listBuckets();
        log.info("全部存储桶信息：{}", listBuckets);
        return listBuckets;
    }

    /**
     * 查询单个储桶中的所有对象。
     *
     * @param bucketName 存储桶名称
     * @throws Exception
     */
    public static List<Object> listObjects(String bucketName) throws Exception {
        Iterable<Result<Item>> results = minioClientInstance.listObjects(ListObjectsArgs.builder()
                .bucket(bucketName)
                .build());
        Iterator<Result<Item>> iterator = results.iterator();
        List<Object> items = new ArrayList<>();
        String format = "{'fileName':'%s','fileSize':'%s'}";
        while (iterator.hasNext()) {
            Item item = iterator.next().get();
            items.add(JSONUtil.parse(String.format(format, item.objectName(),
                    formatFileSize(item.size()))));
        }
        log.info("查询单个储桶中的所有对象信息：{}", items);
        return items;
    }

    /**
     * 删除一个存储桶
     *
     * @param bucketName 存储桶名称
     * @throws Exception
     */
    public static void removeBucket(String bucketName) throws Exception {
        minioClientInstance.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        log.info("删除一个存储桶成功");
    }

    /**
     * 获取对象的元数据
     *
     * @param bucketName 存储桶名称。
     * @param objectName 存储桶里的对象名称
     * @return
     * @throws Exception
     */
    public static StatObjectResponse statObject(String bucketName, String objectName) throws Exception {
        StatObjectResponse statObjectResponse = minioClientInstance.statObject(StatObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
        return statObjectResponse;
    }

    /**
     * 上传文件
     *
     * @param files      文件列表
     * @param bucketName 存储桶名称
     */
    public static List<String> putObject(List<MultipartFile> files, String bucketName) {
        List<String> paths = new ArrayList<>();
        files.forEach(file -> {
            // 获取文件名
            String filename = file.getOriginalFilename();
            paths.add(bucketName + "/" + filename);
            try {
                // 文件上传
                InputStream in = file.getInputStream();
                minioClientInstance.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucketName)
                                .object(filename)
                                .stream(in, file.getSize(), -1)
                                .contentType(file.getContentType())
                                .build());
                in.close();
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        });

        return paths;
    }

    /**
     * 将对象的数据下载到文件中
     *
     * @param bucketName 存储桶名称
     * @param objectName 文件名称
     * @param fileName   文件名称
     * @throws Exception
     */
    public static void downloadObject(String bucketName, String objectName, String fileName) throws Exception {
        minioClientInstance.downloadObject(DownloadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());
    }


    /**
     * 删除文件
     *
     * @param bucketName 存储桶名称
     * @param objectName 文件名称
     * @throws Exception
     */
    public static void removeObject(String bucketName, String objectName) throws Exception {
        minioClientInstance.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 这个 presigned URL可以设置一个失效时间，默认值是7天
     *
     * @param bucketName 存储桶名称
     * @param objectName 文件名称
     * @param expiry     失效时间(单位小时)
     * @return
     */
    public static String getPresignedObjectUrl(String bucketName, String objectName, Integer expiry) throws Exception {
        String presignedObjectUrl = minioClientInstance.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs
                        .builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(objectName)
                        .expiry(expiry, TimeUnit.HOURS)
                        .build());
        log.info("生成http 下载信息：{}", presignedObjectUrl);
        return presignedObjectUrl;
    }

    /**
     * 格式化文件大小
     *
     * @param fileSize 文件长度
     * @return
     */
    public static String formatFileSize(long fileSize) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileSize == 0) {
            return wrongSize;
        }
        if (fileSize < 1024) {
            fileSizeString = df.format((double) fileSize) + " B";
        } else if (fileSize < 1048576) {
            fileSizeString = df.format((double) fileSize / 1024) + " KB";
        } else if (fileSize < 1073741824) {
            fileSizeString = df.format((double) fileSize / 1048576) + " MB";
        } else {
            fileSizeString = df.format((double) fileSize / 1073741824) + " GB";
        }
        return fileSizeString;
    }

}
 
